﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Management.Instrumentation;
using System.Net;

namespace devicesDll
{
    public class Pc : Device
    {
        #region Variables
        string os = null;
        string userAtTime = null;
        UInt64 ram = 0;
        #endregion

        #region Constructor
        public Pc(string macAddress, IPAddress ip, string port)
        {
            this.mac = new string[1];
            this.mac[0] = macAddress;
            this.ip = ip;
            this.port = new string[1];
            this.port[0] = port;
        }
        #endregion

        #region Properties
        public ulong RAM { get { return ram; } }

        public string OS { get { return os; } }

        public string UserAtTime { get { return userAtTime; } }
        #endregion

        #region Methods
        public override void Query(bool switchOnly, string query)
        {
            if (this.ip == null)
            {
                connectionFailed = true;
                return;
            }
            connectionFailed = false;

            if (switchOnly)
                return;

            string[] queries = query.Split(';');
            
            ManagementObjectCollection collection = null;
            ManagementObjectSearcher searcher = new ManagementObjectSearcher();

            #region Connect to PC
            connectionFailed = false;
            try
            {
                ConnectionOptions options = new ConnectionOptions(); //initializes a new Instance of Connection options
                options.Timeout = new TimeSpan(0, 0, 0, 0, timeout); //defines Timeout 
                //options.Username = "Jakob";
                //options.Password = "invIT5a";
                //options.Authority = "NTLMDOMAIN:invit.htl";
                ManagementPath mp = new ManagementPath(); //initializes a new Instance of a Management path
                mp.NamespacePath = @"\root\cimv2"; //default namespace of the WMI query
                mp.Server = this.ip.ToString(); //IP address of the server
                ManagementScope scope = new ManagementScope(mp, options); //initializes a new Instance of Management scope with given Information
                scope.Connect(); //Connect to server, if not avaliable (Access denied, ...) throws an exception
                searcher.Scope = scope;
            }
            catch
            {
                connectionFailed = true;
                return;
            }
            #endregion

            foreach (string q in queries)
            {
                try
                {
                    if (q == "default")
                    {
                        searcher.Scope.Path.NamespacePath = @"\root\cimv2"; //set namespace to default, supportet since Windows XP 
                        
                        #region OS
                        ObjectQuery qOS = new ObjectQuery("SELECT Name FROM Win32_OperatingSystem"); //WQL query for Systemname + filesystempath
                        searcher.Query = qOS; //apply Query
                        collection = searcher.Get(); //get results
                        foreach (ManagementObject var in collection) //foreach variable, only one
                        {
                            os = var["Name"].ToString(); //convert To string
                        }
                        int index = os.LastIndexOf(' '); //search beginning of the Filesystempath
                        os = os.Remove(index);//cut away Filesystempath
                        #endregion

                        #region RAM
                        ObjectQuery qRam = new ObjectQuery("SELECT Capacity FROM Win32_PhysicalMemory"); //WQL query for RAM
                        searcher.Query = qRam; //apply Query
                        collection = searcher.Get(); //get results
                        ram = 0; //set ram to 0
                        foreach (ManagementObject var in collection) //for every ram device one entry
                        {
                            ram += (ulong)var["Capacity"]; //add all rams
                        }
                        #endregion

                        #region Last Logged On User
                        for (int i = 0; i < 5; i++)
                        {
                            #region Calculate Timespan
                            DateTime date = DateTime.Today; //set current date
                            switch (i)
                            {
                                case 0:
                                    date = date.Subtract(new TimeSpan(1, 0, 0, 0)); //search only the last day
                                    break;
                                case 1:
                                    date = date.Subtract(new TimeSpan(7, 0, 0, 0)); //search one week
                                    break;
                                case 2:
                                    date = date.Subtract(new TimeSpan(21, 0, 0, 0)); //search 3 weeks
                                    break;
                                case 3:
                                    date = date.Subtract(new TimeSpan(60, 0, 0, 0)); //search one month
                                    break;
                                case 4:
                                    date = date.Subtract(new TimeSpan(2 * 365, 0, 0, 0)); //search 2 years, nearly forever
                                    break;
                            }
                            #endregion
                            //select NT log entries from logfile security, with the eventcode for logon and in the specific time interval
                            ObjectQuery qUser = new ObjectQuery("SELECT InsertionStrings, TimeWritten FROM Win32_NTLogEvent WHERE Logfile='security' AND EventCode='4648' AND TimeWritten>'" + ManagementDateTimeConverter.ToDmtfDateTime(date) + "'");
                            searcher.Query = qUser; //apply query
                            collection = searcher.Get(); //get results

                            #region filter results
                            bool match = false;
                            foreach (ManagementObject var in collection) //foreache log entry, ordered by date, decreased
                            {
                                string[] ins = (string[])var["InsertionStrings"]; //get message strings
                                if (ins[ins.Length - 3].EndsWith("winlogon.exe")) //if source Service is "winlogon.exe"
                                {
                                    DateTime time = ManagementDateTimeConverter.ToDateTime(var["TimeWritten"].ToString()); //get time
                                    string user = ins[6] + "/" + ins[5]; //save username: "domain/username"
                                    userAtTime = user + "@" + time.ToString(); //save in format "domain/username@time"
                                    match = true; //searched log entry found
                                    break; //breake log entry loop
                                }
                            }
                            if (match) //break date interval loop
                                break;
                            #endregion
                        }
                        #endregion

                    }
                    else //custom query
                    {
                        #region Custom Query

                        #region Namespace
                        string qFinal; //final query string
                        if (q.Contains('@')) //seperator for wmi namespace: §
                        {
                            string[] splitQuery = q.Split('@'); //split at seperator
                            searcher.Scope.Path.NamespacePath = splitQuery[1].Trim(); //last part: namespace, remove leading and trailing whitespaces
                            qFinal = splitQuery[0].Trim(); //first part: query, remove leading and trailing whitespaces
                        }
                        else
                        {
                            searcher.Scope.Path.NamespacePath = @"\root\cimv2"; //if no namespace given: use default namspace
                            qFinal = q.Trim(); //remove leading and trailing whitespaces
                        }
                        #endregion

                        ObjectQuery qCustom = new ObjectQuery(qFinal); //create query object
                        searcher.Query = qCustom; //apply query
                        collection = searcher.Get(); //get results

                        foreach (ManagementObject var in collection) //for every result
                        {
                            foreach (PropertyData item in var.Properties) //for every data
                            {
                                if (item.IsArray) //if data is an array, convert to a string seperated with ';'
                                {
                                    #region convert Array to string
                                    Array itemArray = (Array)item.Value; //cast to an array
                                    if (itemArray != null) //if array is initialized
                                    {
                                        string result = ""; //initialize result string
                                        foreach (var iitem in itemArray)
                                        {
                                            result += iitem.ToString() + ";";
                                        }
                                        if (result.Length > 0)
                                        {
                                            result = result.Remove(result.Length - 1);
                                        }
                                        customResults.Add(new CustomResult(item.Name, result));
                                    }
                                    #endregion
                                }
                                else
                                    customResults.Add(new CustomResult(item.Name, item.Value)); //add to custom results
                            }
                        }
                        
                        #endregion
                    }
                }
                catch (Exception ex)
                {
                    customResults.Add(new CustomResult(q, ex.Message));
                }
            }
        }

        public override string ToString()
        {
            string ipstr;
            if (this.ip == null)
                ipstr = "null";
            else
                ipstr = this.ip.ToString();
            return "PC\n IP: " + ipstr + "\n Mac: " + this.mac[0] + "\n";
        }
        #endregion
    }
}
